#include "ms3d.h"

//-------------------------------------------------------------
//- Constructors
//- 1. Default contructor
//- 2. Load an ms3d (takes a const char *)
//- 3. Destructor
//-------------------------------------------------------------
CMs3d::CMs3d()
{
	m_pVertices = NULL;
	m_pTriangles = NULL;
	m_pMeshes = NULL;
	m_pMaterials = NULL;
	m_pJoints = NULL;
	m_usNumVerts = 0;
	m_usNumTriangles = 0;
	m_usNumMeshes = 0;
	m_usNumMaterials = 0;
	m_usNumJoints = 0;

	m_fTotalTime = 0.0f;
	m_ppSMVB = NULL;
	m_pSMSkinID = NULL;
	//m_bDrawMesh = true;
	//m_bDrawBones = false;
	m_CurrentPath[0] = 0;
	m_CurrentTexName[0] = 0;
}

CMs3d::CMs3d(const char *szFilename)
{
	m_pVertices = NULL;
	m_pTriangles = NULL;
	m_pMeshes = NULL;
	m_pMaterials = NULL;
	m_pJoints = NULL;
	m_usNumVerts = 0;
	m_usNumTriangles = 0;
	m_usNumMeshes = 0;
	m_usNumMaterials = 0;
	m_usNumJoints = 0;

	m_fTotalTime = 0.0f;
	m_ppSMVB = NULL;
	m_pSMSkinID = NULL;

	m_CurrentPath[0] = 0;
	m_CurrentTexName[0] = 0;
	//m_bDrawMesh = true;
	//m_bDrawBones = false;
	//m_Timer.Init();
	Load(szFilename);
}

CMs3d::~CMs3d()
{
	if(m_pVertices)
	{
		delete [] m_pVertices;
		m_pVertices = NULL;
	}
	if(m_pTriangles)
	{
		delete [] m_pTriangles;
		m_pTriangles = NULL;
	}
	if(m_pMeshes)
	{
		delete [] m_pMeshes;
		m_pMeshes = NULL;
	}
	if(m_pMaterials)
	{
		delete [] m_pMaterials;
		m_pMaterials = NULL;
	}
	if(m_pJoints)
	{
		delete [] m_pJoints;
		m_pJoints = NULL;
	}
	if( m_ppSMVB )
	{
		for( int i=0; i<m_usNumMeshes; ++i )
		{
			if( m_ppSMVB[i] )
			{
				delete [] m_ppSMVB[i];
				m_ppSMVB[i] = NULL;
			}
		}

		delete [] m_ppSMVB;
		m_ppSMVB = NULL;
	}
	if( m_pSMSkinID )
	{
		delete [] m_pSMSkinID;
		m_pSMSkinID = NULL;
	}
}


//-------------------------------------------------------------
//- Load	
//- Loads an MS3D file into memory
//-------------------------------------------------------------
bool CMs3d::Load(const char *szFilename)
{
	unsigned char *ucpBuffer = NULL;
	unsigned char *ucpPtr = NULL;
	
	//Open the file
	FILE *f = NULL;
	errno_t err;

	if( (err = fopen_s(&f, szFilename, "rb")) != 0 )
	{
        AlertWindow::showMessageBox( AlertWindow::WarningIcon,
            "Couldn't find the file!",
            "ModelEditor fail to import file :" + String(szFilename) );
		return false;
	}

	_stprintf_s( m_CurrentPath, L"%s", File(szFilename).getParentDirectory().getFullPathName().toWideCharPointer() );
	
	//Get file size
	fseek(f, 0, SEEK_END);
	int iEnd = ftell(f);
	fseek(f, 0, SEEK_SET);
	int iStart = ftell(f);
	unsigned int uiSize = iEnd - iStart;

	//Allocate memory
	ucpBuffer = new unsigned char[uiSize];
	ucpPtr = ucpBuffer;
	if( !ucpBuffer )
	{
        AlertWindow::showMessageBox( AlertWindow::WarningIcon,
            "Couldn't Allocate Memory!",
            "ModelEditor fail to allocate memory for file " + String(szFilename) );
		fclose(f);
		return false;
	}

	//Read file into buffer
	if( fread(ucpBuffer, 1, uiSize, f) != uiSize )
	{
		AlertWindow::showMessageBox( AlertWindow::WarningIcon,
            "Couldn't load the file!",
            "ModelEditor fail to read file " + String(szFilename) );
		delete [] ucpBuffer;
		ucpBuffer = NULL;
		fclose(f);
		return false;
	}

	//close the file, we don't need it anymore
	fclose(f);

	//Check out the header, it should be 10 bytes, MS3D000000
	if( memcmp(ucpPtr, "MS3D000000", 10) != 0 )
	{
		AlertWindow::showMessageBox( AlertWindow::WarningIcon,
            "file error!",
            "File " + String(szFilename) + " is not a valid Milkshape 3D file" );
		delete [] ucpBuffer;
		ucpBuffer = NULL;
		return false;
	}

	//Check the version (should be 3 or 4)
	ucpPtr += 10;
	if( (*(int*)ucpPtr != 3) && (*(int*)ucpPtr != 4) )
	{
		AlertWindow::showMessageBox( AlertWindow::WarningIcon,
            "file error!",
            "File " + String(szFilename) + " is the wrong version, should be 3 or 4" );

		delete [] ucpBuffer;
		ucpBuffer = NULL;
		return false;
	}
	ucpPtr += 4;
	
	//Read the vertices
	//Number of vertices
	m_usNumVerts = *(unsigned short *)ucpPtr;
	ucpPtr += 2;

	//Allocate memory
	m_pVertices = new SMs3dVertex[m_usNumVerts];
	//Copy the vertices
	memcpy(m_pVertices, ucpPtr, m_usNumVerts * sizeof(SMs3dVertex));
	ucpPtr += m_usNumVerts * sizeof(SMs3dVertex);

	//Read the triangles
	m_usNumTriangles = *(unsigned short *)ucpPtr;
	ucpPtr += 2;
	//Alloc memory for triangles
	m_pTriangles = new SMs3dTriangle[m_usNumTriangles];
	//Copy triangles
	memcpy(m_pTriangles, ucpPtr, m_usNumTriangles * sizeof(SMs3dTriangle));
	ucpPtr += m_usNumTriangles * sizeof(SMs3dTriangle);

	//Load mesh groups
	m_usNumMeshes = *(unsigned short *)ucpPtr;
	ucpPtr += 2;
	//Alloc memory for the mesh data
	m_pMeshes = new SMs3dMesh[m_usNumMeshes];
	//Copy the mesh data
	for(int x = 0; x < m_usNumMeshes; x++)
	{
		//Copy the first part of the data
		memcpy(&m_pMeshes[x], ucpPtr, 35);
		ucpPtr += 35;
		//Allocate triangle index memory
		m_pMeshes[x].m_uspIndices = new unsigned short[m_pMeshes[x].m_usNumTris];
		//Copy triangle index data, plus the material index
		memcpy(m_pMeshes[x].m_uspIndices, ucpPtr, m_pMeshes[x].m_usNumTris * sizeof(unsigned short));
		ucpPtr += m_pMeshes[x].m_usNumTris * sizeof(unsigned short);
		m_pMeshes[x].m_cMaterial = ucpPtr[0];
		ucpPtr++;
	}
	
	//Read material information
	m_usNumMaterials = *(unsigned short *)ucpPtr;
	ucpPtr += 2;
	//Alloc memory
	m_pMaterials = new SMs3dMaterial[m_usNumMaterials];
	//Copy material information
	for(int x = 0; x < m_usNumMaterials; x++)
	{
		memcpy(&m_pMaterials[x], ucpPtr, sizeof(SMs3dMaterial));
		ucpPtr += sizeof(SMs3dMaterial);
	}

	//Skip some data we do not need
	ucpPtr += 4;
	ucpPtr += 8;
	
	//Read in joint and animation info
	m_usNumJoints = *(unsigned short *)ucpPtr;
	ucpPtr += 2;
	//Allocate memory
	m_pJoints = new SMs3dJoint[m_usNumJoints];
	//Read in joint info
	for(int x = 0; x < m_usNumJoints; x++)
	{
		memcpy(&m_pJoints[x], ucpPtr, 93);
		ucpPtr += 93;
		//Allocate memory 
		m_pJoints[x].m_RotKeyFrames = new SMs3dKeyFrame[m_pJoints[x].m_usNumRotFrames];
		m_pJoints[x].m_TransKeyFrames = new SMs3dKeyFrame[m_pJoints[x].m_usNumTransFrames];
		//copy keyframe information
		memcpy(m_pJoints[x].m_RotKeyFrames, ucpPtr, m_pJoints[x].m_usNumRotFrames * sizeof(SMs3dKeyFrame));
		ucpPtr += m_pJoints[x].m_usNumRotFrames * sizeof(SMs3dKeyFrame);
		memcpy(m_pJoints[x].m_TransKeyFrames, ucpPtr, m_pJoints[x].m_usNumTransFrames * sizeof(SMs3dKeyFrame));
		ucpPtr += m_pJoints[x].m_usNumTransFrames * sizeof(SMs3dKeyFrame);
		
	}
	
	//Find the parent joint array indices
	for(int x = 0; x < m_usNumJoints; x++)
	{
		//If the bone has a parent
		if(m_pJoints[x].m_cParent[0] != '\0')
		{
			//Compare names of theparent bone of x with the names of all bones
			for(int y = 0; y < m_usNumJoints; y++)
			{
				//A match has been found
				if(strcmp(m_pJoints[y].m_cName, m_pJoints[x].m_cParent) == 0)
				{
					m_pJoints[x].m_sParent = y;
				}
			}
		}
		//The bone has no parent
		else
		{
			m_pJoints[x].m_sParent = -1;
		}
	}
	
	//Setup joints 
	Setup();

	//File loaded
	delete [] ucpBuffer;
	ucpBuffer = NULL;
	return true;
}

//-------------------------------------------------------------
//- Setup
//- Get the joints set up to their beggining positions
//-------------------------------------------------------------
void CMs3d::Setup()
{
	// Go through each joint
	for( int x = 0; x < m_usNumJoints; x++ )
	{
		m_pJoints[x].m_matLocal.RotationXYZ(m_pJoints[x].m_fStartRotation[0], m_pJoints[x].m_fStartRotation[1], m_pJoints[x].m_fStartRotation[2]);
		m_pJoints[x].m_matLocal.Translate(m_pJoints[x].m_fStartPosition[0], m_pJoints[x].m_fStartPosition[1], m_pJoints[x].m_fStartPosition[2]);
		
		// Set the Abs transformations to the parents transformations, combined with their own local ones
		if(m_pJoints[x].m_sParent != -1)
		{
			m_pJoints[x].m_matAbs = m_pJoints[x].m_matLocal * m_pJoints[m_pJoints[x].m_sParent].m_matAbs;
		}		
		else // If there is no parent
		{
			m_pJoints[x].m_matAbs = m_pJoints[x].m_matLocal;
		}

		m_pJoints[x].m_matFinal = m_pJoints[x].m_matAbs;
	}

	// Go through each vertex
	for( int x = 0; x < m_usNumVerts; x++ )
	{
		//If there is no bone..
		if( m_pVertices[x].m_cBone == -1 )
			continue;

		OyVector4D Vert( m_pVertices[x].m_vVert[0] - m_pJoints[m_pVertices[x].m_cBone].m_matFinal._41,
			m_pVertices[x].m_vVert[1] - m_pJoints[m_pVertices[x].m_cBone].m_matFinal._42,
			m_pVertices[x].m_vVert[2] - m_pJoints[m_pVertices[x].m_cBone].m_matFinal._43 );
		m_pJoints[m_pVertices[x].m_cBone].m_matFinal.ApplyInverseRotation( &Vert );

		m_pVertices[x].m_vVert[0] = Vert.x;
		m_pVertices[x].m_vVert[1] = Vert.y;
		m_pVertices[x].m_vVert[2] = Vert.z;
	}

	//Go through the normals and transform them
	for( int x = 0; x < m_usNumTriangles; x++ )
	{
		SMs3dTriangle *pTri = &m_pTriangles[x];

		//Loop through each index
		for(int z = 0; z < 3; z++)
		{
			//Get the vertex so we can find out what joint we need to use to transform the normal
			SMs3dVertex *pVert = &m_pVertices[pTri->m_usVertIndices[z]];
			
			//if it is not attached to a bone, don't do any transforms
			if(pVert->m_cBone == -1)
				continue;

			SMs3dJoint *pJoint = &m_pJoints[pVert->m_cBone];
			
			//Transform the normal
			OyVector4D Norm(pTri->m_vNormals[z]);
			pJoint->m_matFinal.ApplyInverseRotation( &Norm );

			pTri->m_vNormals[z][0] = Norm.x;
			pTri->m_vNormals[z][1] = Norm.y;
			pTri->m_vNormals[z][2] = Norm.z;
		}
	}

	// Go through each Joint, find out Total Anim Time
	m_fTotalTime = 0.0f;
	for(int x = 0; x < m_usNumJoints; x++)
	{
		for(int j=0; j<m_pJoints[x].m_usNumRotFrames; j++)
			m_fTotalTime = MAX(m_fTotalTime, m_pJoints[x].m_RotKeyFrames[j].m_fTime);
		for(int j=0; j<m_pJoints[x].m_usNumTransFrames; j++)
			m_fTotalTime = MAX(m_fTotalTime, m_pJoints[x].m_TransKeyFrames[j].m_fTime);
	}

	// Go through each material and create skin
	if( m_usNumMaterials > 0 )
	{
		if( m_pSMSkinID )
			delete [] m_pSMSkinID;
		m_pSMSkinID = new UINT [m_usNumMaterials];
		for(int x = 0; x < m_usNumMaterials; x++)
		{
			OyColor ambient(m_pMaterials[x].m_fAmbient[0], m_pMaterials[x].m_fAmbient[1], m_pMaterials[x].m_fAmbient[2], m_pMaterials[x].m_fAmbient[3] );
			OyColor diffuse(m_pMaterials[x].m_fDiffuse[0], m_pMaterials[x].m_fDiffuse[1], m_pMaterials[x].m_fDiffuse[2], m_pMaterials[x].m_fDiffuse[3] );
			OyColor specular(m_pMaterials[x].m_fSpecular[0], m_pMaterials[x].m_fSpecular[1], m_pMaterials[x].m_fSpecular[2], m_pMaterials[x].m_fSpecular[3] );
			OyColor emissive(m_pMaterials[x].m_fEmissive[0], m_pMaterials[x].m_fEmissive[1], m_pMaterials[x].m_fEmissive[2], m_pMaterials[x].m_fEmissive[3] );
			g_pDevice->GetSkinManager()->AddSkin(&ambient, &diffuse, &emissive, &specular, m_pMaterials[x].m_fShininess, &(m_pSMSkinID[x]));
			g_pDevice->GetSkinManager()->SetAlphaToSkin(m_pSMSkinID[x], (m_pMaterials[x].m_fTransparency < 1.0f));
		}
	}
}


// ------------------------------------------------------------
//- Name: SetScaling( float fScale /* = 0.0f  */ )
//- Info: Sets the scaling of the model. 0.0f for no scaling.
//-------------------------------------------------------------
void CMs3d::SetScaling( float fScale )
{
	SMs3dVertex		*pVertex      = NULL;		// TempVertex
	OyAABB			BoundingBox;				// BBox
	if( fScale == 0.0f )
		return;

	// get the bounding box
	// reset the min and max vertex of the BoundingBox
	BoundingBox.vcMin.x = 999999.0f;
	BoundingBox.vcMin.y = 999999.0f;
	BoundingBox.vcMin.z = 999999.0f;
	BoundingBox.vcMax.x = -999999.0f;
	BoundingBox.vcMax.y = -999999.0f;
	BoundingBox.vcMax.z = -999999.0f;

	// Now loop all known vertices and get BoundingBox
	// Go through each vertex
	for( int x = 0; x < m_usNumVerts; x++ )
	{
		BoundingBox.vcMax.x = MAX( BoundingBox.vcMax.x, m_pVertices[x].m_vVert[0] );
		BoundingBox.vcMax.y = MAX( BoundingBox.vcMax.y, m_pVertices[x].m_vVert[1] );
		BoundingBox.vcMax.z = MAX( BoundingBox.vcMax.z, m_pVertices[x].m_vVert[2] );
		BoundingBox.vcMin.x = MIN( BoundingBox.vcMin.x, m_pVertices[x].m_vVert[0] );
		BoundingBox.vcMin.y = MIN( BoundingBox.vcMin.y, m_pVertices[x].m_vVert[1] );
		BoundingBox.vcMin.z = MIN( BoundingBox.vcMin.z, m_pVertices[x].m_vVert[2] );
	}

	// Calc scaling
	m_ModelScale = fScale / ( BoundingBox.vcMax.y - BoundingBox.vcMin.y );

	return;
}

//-------------------------------------------------------------
//- GetAnimTime
//- Get the time of Anim named "szAnimName"
//-------------------------------------------------------------
void CMs3d::GetAnimTime(const char *szAnimName, float &AnimTimeStart, float &AnimTimeEnd)
{
	AnimTimeStart = 0;
	AnimTimeEnd = m_fTotalTime;
}

//-------------------------------------------------------------
//- BindTexture
//- Bind correct texture files to according material skin in the ms3d files itself
//-------------------------------------------------------------
void CMs3d::BindTexture()
{
	if( m_pSMSkinID && (m_usNumMaterials > 0) )
	{
		for(int x = 0; x < m_usNumMaterials; x++)
		{
			if( m_pMaterials[x].m_cTexture[0] != '\0' )
			{
				_stprintf_s( m_CurrentTexName, L"%s\\%s", m_CurrentPath, String(m_pMaterials[x].m_cTexture+2).toWideCharPointer() );
				if( BindCorrectTextureName(m_CurrentTexName) )
					g_pDevice->GetSkinManager()->AddDeferedTexture(m_pSMSkinID[x], m_CurrentTexName, 0);
			}
		}
	}
}

//-------------------------------------------------------------
//- BindCorrectTextureName
//- Texture file name in struct SMs3dMaterial usually has error file's extension,
//- this function find correct file's extension in local path
//-------------------------------------------------------------
bool CMs3d::BindCorrectTextureName(TCHAR (&TexName) [128])
{
	if( File(TexName).exists() )
		return true;
	String FileName = File(TexName).getFileNameWithoutExtension();
	String FilePath(m_CurrentPath);

	if( File(String(m_CurrentPath)+String("\\")+FileName+String(".bmp")).exists() )
	{
		_stprintf_s( TexName, L"%s", String(String(m_CurrentPath)+String("\\")+FileName+String(".bmp")).toWideCharPointer() );
		return true;
	}
	if( File(String(m_CurrentPath)+String("\\")+FileName+String(".jpg")).exists() )
	{
		_stprintf_s( TexName, L"%s", String(String(m_CurrentPath)+String("\\")+FileName+String(".jpg")).toWideCharPointer() );
		return true;
	}
	if( File(String(m_CurrentPath)+String("\\")+FileName+String(".png")).exists() )
	{
		_stprintf_s( TexName, L"%s", String(String(m_CurrentPath)+String("\\")+FileName+String(".png")).toWideCharPointer() );
		return true;
	}
	if( File(String(m_CurrentPath)+String("\\")+FileName+String(".tga")).exists() )
	{
		_stprintf_s( TexName, L"%s", String(String(m_CurrentPath)+String("\\")+FileName+String(".tga")).toWideCharPointer() );
		return true;
	}
	if( File(String(m_CurrentPath)+String("\\")+FileName+String(".dds")).exists() )
	{
		_stprintf_s( TexName, L"%s", String(String(m_CurrentPath)+String("\\")+FileName+String(".dds")).toWideCharPointer() );
		return true;
	}
	return false;
}

//-------------------------------------------------------------
//- CreateStaticMesh
//- Create the model StaticMesh in its initial position (frame 0)
//- If no material in this ms3d file, DefaultSkinID will use as skin
//-------------------------------------------------------------
void CMs3d::CreateStaticMesh(UINT DefaultSkinID)
{
	if( m_usNumMeshes <= 0 )
		return;

	if( m_ppSMVB )
	{
		for( int i=0; i<m_usNumMeshes; ++i )
		{
			if( m_ppSMVB[i] )
			{
				delete [] m_ppSMVB[i];
				m_ppSMVB[i] = NULL;
			}
		}

		delete [] m_ppSMVB;
		m_ppSMVB = NULL;
	}

	//Transformed vertices and normals
	OyVector4D vecNormal;
	OyVector4D vecVertex;
	

	m_ppSMVB = new OY_UNORMALCOLORTEX_VERTEX_TYPE* [m_usNumMeshes];

	for(int x = 0; x < m_usNumMeshes; x++)
	{
		float Alpha = 1.0f;
		float Diffuse[3] = { 1.0f };
		m_ppSMVB[x] = new OY_UNORMALCOLORTEX_VERTEX_TYPE [m_pMeshes[x].m_usNumTris * 3];

		UINT skinID = DefaultSkinID;
		if(m_pMeshes[x].m_cMaterial >= 0)
		{  
			SMs3dMaterial *pCurMat = &m_pMaterials[m_pMeshes[x].m_cMaterial];
			Alpha = pCurMat->m_fTransparency;
			Diffuse[0] = pCurMat->m_fDiffuse[0];
			Diffuse[1] = pCurMat->m_fDiffuse[1];
			Diffuse[2] = pCurMat->m_fDiffuse[2];
			skinID = m_pSMSkinID[m_pMeshes[x].m_cMaterial];
		}

		//Loop through triangles
		for(int y = 0; y < m_pMeshes[x].m_usNumTris; y++)
		{
			SMs3dTriangle *pTri = &m_pTriangles[m_pMeshes[x].m_uspIndices[y]];

			for(int z = 0; z < 3; z++)
			{
				//Get the vertex
				SMs3dVertex *pVert = &m_pVertices[pTri->m_usVertIndices[z]];
				//If it has no bone, render as is
				if(pVert->m_cBone == -1)
				{
					m_ppSMVB[x][y*3+z].x = pVert->m_vVert[0] * m_ModelScale;
					m_ppSMVB[x][y*3+z].y = pVert->m_vVert[1] * m_ModelScale;
					m_ppSMVB[x][y*3+z].z = pVert->m_vVert[2] * m_ModelScale;
					m_ppSMVB[x][y*3+z].vcNormal[0] = pTri->m_vNormals[z][0];
					m_ppSMVB[x][y*3+z].vcNormal[1] = pTri->m_vNormals[z][1];
					m_ppSMVB[x][y*3+z].vcNormal[2] = pTri->m_vNormals[z][2];
					m_ppSMVB[x][y*3+z].VertexColor = ((DWORD)(Alpha*255) << 24) |
						((DWORD)(Diffuse[0]*255) << 16) | ((DWORD)(Diffuse[1]*255) << 8) |
						(DWORD)(Diffuse[2]*255);
					m_ppSMVB[x][y*3+z].tu = pTri->m_fTexCoords[0][z];
					m_ppSMVB[x][y*3+z].tv = pTri->m_fTexCoords[1][z];
				}
				else
				{
					SMs3dJoint *pJoint = &m_pJoints[pVert->m_cBone];
					vecNormal.Set(pTri->m_vNormals[z][0], pTri->m_vNormals[z][1], pTri->m_vNormals[z][2]);
					vecNormal.RotateWith(pJoint->m_matFinal);
					vecVertex.Set(pVert->m_vVert[0], pVert->m_vVert[1], pVert->m_vVert[2]);
					vecVertex = vecVertex * pJoint->m_matFinal;

					m_ppSMVB[x][y*3+z].x = vecVertex.x * m_ModelScale;
					m_ppSMVB[x][y*3+z].y = vecVertex.y * m_ModelScale;
					m_ppSMVB[x][y*3+z].z = vecVertex.z * m_ModelScale;
					m_ppSMVB[x][y*3+z].vcNormal[0] = vecNormal.x;
					m_ppSMVB[x][y*3+z].vcNormal[1] = vecNormal.y;
					m_ppSMVB[x][y*3+z].vcNormal[2] = vecNormal.z;
					m_ppSMVB[x][y*3+z].VertexColor = ((DWORD)(Alpha*255) << 24) |
						((DWORD)(Diffuse[0]*255) << 16) | ((DWORD)(Diffuse[1]*255) << 8) |
						(DWORD)(Diffuse[2]*255);
					m_ppSMVB[x][y*3+z].tu = pTri->m_fTexCoords[0][z];
					m_ppSMVB[x][y*3+z].tv = pTri->m_fTexCoords[1][z];
				}
			}// end of z
		}// end of y

		g_pDevice->GetVertexCacheManager()->DeferedCreateStaticBuffer( VID_UNORCOLTEX, skinID, m_pMeshes[x].m_usNumTris*3, 0, m_ppSMVB[x], NULL );

	}// end of x

}


void CMs3d::Render(float ElapsedSeconds)
{
	RenderAnimate( g_DefaultSkin, ElapsedSeconds, 
		CModelEditorConfig::GetInstance()->m_fAnimPlayingSpeed.get(),
		CModelEditorConfig::GetInstance()->m_UIStartTime.get(),
		CModelEditorConfig::GetInstance()->m_UIEndTime.get(),
		CModelEditorConfig::GetInstance()->m_bLoopPlayMode );
}

//-------------------------------------------------------------
//- RenderAnimate
//- Animates the model from start time to end time (in seconds) at specified speed and can loop
//-------------------------------------------------------------
void CMs3d::RenderAnimate(UINT DefaultSkinID, float ElapsedSeconds, float fSpeed, float fStartTime, float fEndTime, bool bLoop)
{
	static float fLastTime = fStartTime;

	float fTime = ElapsedSeconds * fSpeed;
	fTime += fLastTime;
	fLastTime = fTime;

	//looping
	if(fTime > fEndTime)
	{
		if(bLoop)
		{
			fLastTime = fStartTime;
			fTime = fStartTime;
		}
		else
			fTime = fEndTime;
	}

	CModelEditorConfig::GetInstance()->m_CurrentAnimTime = fTime;

	for(int x = 0; x < m_usNumJoints; x++)
	{
		SMs3dJoint *pJoint = &m_pJoints[x];

		//if there are no keyframes, don't do any transformations
		if( (pJoint->m_usNumRotFrames == 0) && (pJoint->m_TransKeyFrames == 0) )
		{
			pJoint->m_matFinal = pJoint->m_matAbs;
			continue;
		}

		//Current frame
		unsigned int uiFrame = 0;

		//Calculate the current Translation frame
		while((uiFrame < pJoint->m_usNumTransFrames) && (pJoint->m_TransKeyFrames[uiFrame].m_fTime < fTime))
			uiFrame++;
		//pJoint->m_usCurTransFrame = uiFrame;

		float fTranslation[3];
		float fDeltaT = 1.0f;
		float fInterp = 0;

		//If its at the extremes
		if(uiFrame == 0)
			memcpy(fTranslation, pJoint->m_TransKeyFrames[0].m_fParam, sizeof(float[3]));
		else if(uiFrame == pJoint->m_usNumTransFrames)
			memcpy(fTranslation, pJoint->m_TransKeyFrames[uiFrame-1].m_fParam, sizeof(float[3]));
		else
		{
			//If its in the middle of two frames
			SMs3dKeyFrame *pkCur = &pJoint->m_TransKeyFrames[uiFrame];
			SMs3dKeyFrame *pkPrev = &pJoint->m_TransKeyFrames[uiFrame-1];
			
			fDeltaT = pkCur->m_fTime - pkPrev->m_fTime;
			fInterp = (fTime - pkPrev->m_fTime) / fDeltaT;
			
			//Interpolate between the translations
			fTranslation[0] = pkPrev->m_fParam[0] + (pkCur->m_fParam[0] - pkPrev->m_fParam[0]) * fInterp;
			fTranslation[1] = pkPrev->m_fParam[1] + (pkCur->m_fParam[1] - pkPrev->m_fParam[1]) * fInterp;
			fTranslation[2] = pkPrev->m_fParam[2] + (pkCur->m_fParam[2] - pkPrev->m_fParam[2]) * fInterp;
		}

		//Transformation matrix
		OyMatrix matTmp;

		//Calculate the current rotation
		uiFrame = 0;
		while((uiFrame < pJoint->m_usNumRotFrames) && (pJoint->m_RotKeyFrames[uiFrame].m_fTime < fTime))
			uiFrame++;

		//If its at the extremes
		if(uiFrame == 0)
			matTmp.RotationXYZ(pJoint->m_RotKeyFrames[0].m_fParam[0], pJoint->m_RotKeyFrames[0].m_fParam[1], pJoint->m_RotKeyFrames[0].m_fParam[2]);
		else if(uiFrame == pJoint->m_usNumTransFrames)
			matTmp.RotationXYZ(pJoint->m_RotKeyFrames[uiFrame-1].m_fParam[0], pJoint->m_RotKeyFrames[uiFrame-1].m_fParam[1], pJoint->m_RotKeyFrames[uiFrame-1].m_fParam[2]);
		else
		{
			//If its in the middle of two frames, use a quaternion SLERP operation to calculate a new position
			SMs3dKeyFrame *pkCur = &pJoint->m_RotKeyFrames[uiFrame];
			SMs3dKeyFrame *pkPrev = &pJoint->m_RotKeyFrames[uiFrame-1];

			fDeltaT = pkCur->m_fTime - pkPrev->m_fTime;
			fInterp = (fTime - pkPrev->m_fTime) / fDeltaT;
			
			//Create a rotation quaternion for each frame
			OyQuaternion qCur;
			OyQuaternion qPrev;
			qCur.MakeFromEuler(pkCur->m_fParam[0], pkCur->m_fParam[1], pkCur->m_fParam[2]);
			qPrev.MakeFromEuler(pkPrev->m_fParam[0], pkPrev->m_fParam[1], pkPrev->m_fParam[2]);
			//SLERP between the two frames
			OyQuaternion qFinal = qPrev.Slerp(qCur, fInterp);

			//Convert the quaternion to a rotation matrix
			qFinal.GetMatrix(&matTmp);
			OyMatrix m1 = matTmp;
			matTmp.TransposeOf(m1);
		}

		//Set the translation part of the matrix
		matTmp.Translate(fTranslation[0], fTranslation[1], fTranslation[2]);


		//Calculate the joints final transformation
		OyMatrix matFinal = matTmp * pJoint->m_matLocal;
		
		if(pJoint->m_sParent == -1)
		{
			//if there is no parent, just use the matrix you just made
			pJoint->m_matFinal = matFinal;
		}
		else
		{
			pJoint->m_matFinal = matFinal * m_pJoints[pJoint->m_sParent].m_matFinal;
		}
	}

	RenderTransVert(DefaultSkinID);
}

//-------------------------------------------------------------
//- Transform and render the meshes
//-------------------------------------------------------------
void CMs3d::RenderTransVert(UINT DefaultSkinID)
{
	if( m_usNumMeshes <= 0 )
		return;

	//Transformed vertices and normals
	OyVector4D vecNormal;
	OyVector4D vecVertex;

	if( !m_ppSMVB )
		m_ppSMVB = new OY_UNORMALCOLORTEX_VERTEX_TYPE* [m_usNumMeshes];

	for(int x = 0; x < m_usNumMeshes; x++)
	{
		float Alpha = 1.0f;
		float Diffuse[3] = { 1.0f };

		if( !m_ppSMVB[x] )
			m_ppSMVB[x] = new OY_UNORMALCOLORTEX_VERTEX_TYPE [m_pMeshes[x].m_usNumTris * 3];

		UINT skinID = DefaultSkinID;
		if(m_pMeshes[x].m_cMaterial >= 0)
		{  
			SMs3dMaterial *pCurMat = &m_pMaterials[m_pMeshes[x].m_cMaterial];
			Alpha = pCurMat->m_fTransparency;
			Diffuse[0] = pCurMat->m_fDiffuse[0];
			Diffuse[1] = pCurMat->m_fDiffuse[1];
			Diffuse[2] = pCurMat->m_fDiffuse[2];
			skinID = m_pSMSkinID[m_pMeshes[x].m_cMaterial];
		}
		//Loop through triangles
		for(int y = 0; y < m_pMeshes[x].m_usNumTris; y++)
		{
			SMs3dTriangle *pTri = &m_pTriangles[m_pMeshes[x].m_uspIndices[y]];

			for(int z = 0; z < 3; z++)
			{
				//Get the vertex
				SMs3dVertex *pVert = &m_pVertices[pTri->m_usVertIndices[z]];
				//If it has no bone, render as is
				if(pVert->m_cBone == -1)
				{
					m_ppSMVB[x][y*3+z].x = pVert->m_vVert[0] * m_ModelScale;
					m_ppSMVB[x][y*3+z].y = pVert->m_vVert[1] * m_ModelScale;
					m_ppSMVB[x][y*3+z].z = pVert->m_vVert[2] * m_ModelScale;
					m_ppSMVB[x][y*3+z].vcNormal[0] = pTri->m_vNormals[z][0];
					m_ppSMVB[x][y*3+z].vcNormal[1] = pTri->m_vNormals[z][1];
					m_ppSMVB[x][y*3+z].vcNormal[2] = pTri->m_vNormals[z][2];
					m_ppSMVB[x][y*3+z].VertexColor = ((DWORD)(Alpha*255) << 24) |
						((DWORD)(Diffuse[0]*255) << 16) | ((DWORD)(Diffuse[1]*255) << 8) |
						(DWORD)(Diffuse[2]*255);
					m_ppSMVB[x][y*3+z].tu = pTri->m_fTexCoords[0][z];
					m_ppSMVB[x][y*3+z].tv = pTri->m_fTexCoords[1][z];
				}
				else
				{
					SMs3dJoint *pJoint = &m_pJoints[pVert->m_cBone];
					vecNormal.Set(pTri->m_vNormals[z][0], pTri->m_vNormals[z][1], pTri->m_vNormals[z][2]);
					vecNormal.RotateWith(pJoint->m_matFinal);
					vecVertex.Set(pVert->m_vVert[0], pVert->m_vVert[1], pVert->m_vVert[2]);
					vecVertex = vecVertex * pJoint->m_matFinal;

					m_ppSMVB[x][y*3+z].x = vecVertex.x * m_ModelScale;
					m_ppSMVB[x][y*3+z].y = vecVertex.y * m_ModelScale;
					m_ppSMVB[x][y*3+z].z = vecVertex.z * m_ModelScale;
					m_ppSMVB[x][y*3+z].vcNormal[0] = vecNormal.x;
					m_ppSMVB[x][y*3+z].vcNormal[1] = vecNormal.y;
					m_ppSMVB[x][y*3+z].vcNormal[2] = vecNormal.z;
					m_ppSMVB[x][y*3+z].VertexColor = ((DWORD)(Alpha*255) << 24) |
						((DWORD)(Diffuse[0]*255) << 16) | ((DWORD)(Diffuse[1]*255) << 8) |
						(DWORD)(Diffuse[2]*255);
					m_ppSMVB[x][y*3+z].tu = pTri->m_fTexCoords[0][z];
					m_ppSMVB[x][y*3+z].tv = pTri->m_fTexCoords[1][z];
				}
			}// end of z
		}// end of y

		g_pDevice->GetVertexCacheManager()->RenderDynamicBuffer( VID_UNORCOLTEX, skinID, m_pMeshes[x].m_usNumTris*3, 0, m_ppSMVB[x], NULL );
	}// end of x
}
